/*
 *  Copyright 2020 Huawei Technologies Co., Ltd.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.edgegallery.mecm.apm.exception;

import org.edgegallery.mecm.apm.utils.ErrorCode;
import org.edgegallery.mecm.apm.utils.ResponseObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.validation.ConstraintViolationException;
import javax.ws.rs.ForbiddenException;
import java.nio.file.AccessDeniedException;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

@ControllerAdvice
public class ApmExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApmExceptionHandler.class);

    /**
     * Returns response entity with error details when input validation is failed.
     *
     * @param ex exception while validating input
     * @return response entity with error details
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ResponseObject> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
        List<String> errorMsg = new ArrayList<>();
        if (ex.getBindingResult().hasErrors()) {
            ex.getBindingResult().getAllErrors().forEach(error -> errorMsg.add(error.getDefaultMessage()));
        }
        LOGGER.info("Method argument error: {}", String.join(";", errorMsg));
        return ResponseEntity.ok(new ResponseObject<>(String.join(";", errorMsg), ErrorCode.ErrInvalidReq, ErrorCode.FailMessage));
    }

    /**
     * Returns response entity with error details when input validation is failed.
     *
     * @param ex exception while validating input
     * @return response entity with error details
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ResponseObject> handleConstraintViolationException(ConstraintViolationException ex) {
        LOGGER.info("Constraint violation error: {}", ex.getMessage());
        return ResponseEntity.ok(new ResponseObject<>(null,ErrorCode.ErrInvalidReq, ErrorCode.FailMessage));
    }

    /**
     * Returns error when access is denied.
     *
     * @param ex exception while processing request
     * @return response entity with error code and message
     */
    @ExceptionHandler(ForbiddenException.class)
    public ResponseEntity<ResponseObject> handleForbiddenException(ForbiddenException ex) {
        LOGGER.info("User is not authorized to perform this operation: {}", ex.getMessage());
        return ResponseEntity.ok(new ResponseObject(null, ErrorCode.ErrForbidden, ErrorCode.FailMessage));
    }

    /**
     * Returns error code and message when record not found.
     *
     * @param ex exception while processing request
     * @return response entity with error code and message
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<ResponseObject> handleRuntimeException(RuntimeException ex) {
        LOGGER.info("Internal server error: {}", ex.getMessage());
        return ResponseEntity.ok(new ResponseObject(null, ErrorCode.ErrInternal, ErrorCode.FailMessage));
    }

    /**
     * Returns error code and message when record not found.
     *
     * @param ex exception while processing request
     * @return response entity with error code and message
     */
    @ExceptionHandler(NoSuchElementException.class)
    public ResponseEntity<ResponseObject> handleNoSuchElementException(NoSuchElementException ex) {
        LOGGER.info("No such element error: {}", ex.getMessage());
        return ResponseEntity.ok(new ResponseObject(null,ErrorCode.ErrNotFound, ErrorCode.FailMessage));
    }
}